变量作用域和内存问题

1.基本类型和引用类型的值

基本类型就是简单的数据段(5种值类型),而引用类型就是对象(操控对象的引用)。

1.1复制变量值

引用类型实际上在复制的时候,传递的是函数的指针,复制完成后,实际两个变量引用的都是同一个堆内存中的对象,改变这个对象,两个变量的值也会同步改变。

1.2传递参数

函数的参数都是按值传递的。其实我认为这种说法多少还是有些抽象。总结起来不如这样说。当传递给函数的变量是值类型时,那么传递给函数的这个原始变量的不会随函数内部的影响而改变。当传递给函数的变量是引用类型(object)时,那么传递给函数的这个原始变量的引用不会随函数内部的影响而改变。其实这里不是太容易理解,比如举个例子来说明

        var obj = {
            name: 'andy'
        }

        function ChangeObj(val) {
            val.age = '25'
            val = {
                name:'zakas',
                age:40
            }
            return val;
        }
        ChangeObj(obj); // {name:'zakas',age:40}
        console.log(obj) // {name:'andy',age:25}
        
      

上面这个例子中,函数中val的内存地址变了(引用变了),如果函数是按引用传递的话,那么val的引用就是obj的引用,val的应用变了,obj的引用也会跟随变化,所以obj的结果也应该是{name:'zakas',age:40}。而按值传递的话,obj赋值给val它的引用,但是他俩的引用是不关联在一起的,val引用的改变并不会影响obj的引用地址。

函数的形参就是函数作用域中的局部变量。当函数运行完是会被销毁的。

1.3检测类型

instanceof 可以进行引用类型的更明确的检测。所以值类型在instanceof总全是false,值类型也没必要用其方法进行检测。instanceof只能区分Array Object 和 RegExp

2.执行环境

每个执行环境都有一个变量对象,变量对象的概念很重要。它是作用域中所有定义的变量的一个大的集合;每个执行环境都有一个变量对象,存储着我们定义的所有变量,但是这个变量对象我们访问不到,但解析器处理数据时会在后台使用它。

    function A() {
        var tempA;
        function B() {
            var tempB;
            function C() {
                var tempC
            }
        }
    }

B()的作用域链中包含3个对象,一个是自己的变量对象,还有A的变量对象和全局变量对象。A()中的作用域链包含2个对象,一个A()自己的变量对象,还有就是全局变量对象。因此A访问不了B的变量,只能访问自己的和全局的变量。但是B不但能访问自己的变量,也能访问A和全局作用域下的变量。

2.1延长作用域链

with

With相当于创造了一个新的变量对象在当前作用域的上方。例如:

    var obj = {
        name:'andy',
        sex:'man',
        hobby:'game'
    }
    function fn() {
        with(obj) {
            fnName = name;
            fnSex = sex;
            fnHobby = hobby;
        }
        console.log(fnName,fnSex,fnHobby) // andy,man,game
    }
    fn()

With方法会造成性能的严重损失,所以一般不建议用

2.2无块级作用域

必须要知道这个概念,作用和区别,最老生常谈的一个问题:

for(var i =0 ; i < 10 ; i++ ) {
    setTimeout(function(){
        console.log(i);
    },0)
}

在理解这个问题的前提下,首先要知道定时器是异步的,即使是0,也要先放到缓存区中,当其他程序自上而下执行完毕之后再去调用。所以在其他程序自上而下执行完毕之后,由于没有块级作用域,i是全局的,已经变成10了,所以输出10个10。如果把i改为存在块级作用域的let,那么问题就迎刃而解了。

for(let i =0 ; i < 10 ; i++ ) {
    setTimeout(function(){
        console.log(i);
    },0)
}

2.3垃圾收集

JavaScript具有自动垃圾收集机制

使用值的过程中,其实是相当于对变量分配的内存进行写入和读取的操作。JavaScript在创建变量的过程会分配内存,当变量不用时会自动释放掉,这个过程叫做垃圾回收机制,但是这个自动是混乱的根源,很多开发者因此觉得不用太关心内存问题,这是错误的。

原理:垃圾处理器会在默认情况下周期的进行检测,找出那些不用的变量,然后释放其内存。
回收策略:在局部环境中,函数调用完之后,垃圾收集器会跟踪哪个变量有用,哪个变量没有用,对于没用的变量,打上标记,以备回收其内存,但是标记的方法通常有另两个策略:

  • 标记清除:从2012年起,所有浏览器全部使用标记清除的方法进行垃圾回收,并且所有对js垃圾回收的改进也是基于标记清除的方法进行算法优化。
  • 引用计数:这个由于存在严重的循环引用问题,所以现在已经基本不用了。

因此,当写的程序占用较少的内存是高性能页面的一个很重要的点。所以当我们写程序时候,一旦数据不用了,最好设置成null来解除引用


andy00614
101 声望2 粉丝